Оставить соединение с пунктом «Где» - PullRequest
131 голосов
/ 20 января 2011

Мне нужно получить все настройки по умолчанию из таблицы настроек, но также получить настройки символов, если они существуют для символа x.

Но этот запрос извлекает только те настройки, где символ = 1, а не настройки по умолчанию, если пользователь никого не установил.

SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT JOIN `character_settings` 
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1'  

Так что мне нужно что-то вроде этого:*

array(
    '0' => array('somekey' => 'keyname', 'value' => 'thevalue'),
    '1' => array('somekey2' => 'keyname2'),
    '2' => array('somekey3' => 'keyname3')
)

Где ключи 1 и 2 являются значениями по умолчанию, когда ключ 0 содержит значение по умолчанию со значением символа.

Ответы [ 5 ]

294 голосов
/ 20 января 2011

Предложение where отфильтровывает строки, в которых left join не удается. Переместить его в соединение:

SELECT  `settings`.*, `character_settings`.`value`
FROM    `settings`
LEFT JOIN 
       `character_settings` 
ON     `character_settings`.`setting_id` = `settings`.`id`
        AND `character_settings`.`character_id` = '1'  
50 голосов
/ 21 января 2011

При создании ВНЕШНИХ СОЕДИНЕНИЙ (ANSI-89 или ANSI-92) местоположение фильтрации имеет значение, поскольку критерии, указанные в предложении ON, применяются до создания СОЕДИНЕНИЯ .Критерии для таблицы OUTER JOINed, представленные в предложении WHERE, применяются после создания JOIN .Это может привести к очень разным наборам результатов.Для сравнения, для ВНУТРЕННИХ СОЕДИНЕНИЙ не имеет значения, указаны ли критерии в предложениях ON или WHERE - результат будет таким же.

  SELECT  s.*, 
          cs.`value`
     FROM SETTINGS s
LEFT JOIN CHARACTER_SETTINGS cs ON cs.setting_id = s.id
                               AND cs.character_id = 1
17 голосов
/ 21 января 2011

Если я правильно понимаю ваш вопрос, вам нужны записи из базы данных настроек, если у них нет объединения по таблице character_settings или если у этой объединенной записи есть character_id = 1.

Поэтому вы должны сделать

SELECT `settings`.*, `character_settings`.`value`
FROM (`settings`)
LEFT OUTER JOIN `character_settings` 
ON `character_settings`.`setting_id` = `settings`.`id`
WHERE `character_settings`.`character_id` = '1' OR
`character_settings`.character_id is NULL
2 голосов
/ 21 января 2011

Возможно, вам будет проще понять, используя простой подзапрос

SELECT `settings`.*, (
    SELECT `value` FROM `character_settings`
    WHERE `character_settings`.`setting_id` = `settings`.`id`
      AND `character_settings`.`character_id` = '1') AS cv_value
FROM `settings`

Подзапросу разрешено возвращать ноль, поэтому вам не нужно беспокоиться о JOIN / WHERE в основном запросе.

Иногда это работает быстрее в MySQL, но сравните его с формой LEFT JOIN, чтобы увидеть, что работает лучше для вас.

SELECT s.*, c.value
FROM settings s
LEFT JOIN character_settings c ON c.setting_id = s.id AND c.character_id = '1'
1 голос
/ 12 июля 2017

Результат правильный на основании оператора SQL. Левое соединение возвращает все значения из правой таблицы и только совпадающие значения из левой таблицы.

Столбцы ID и NAME взяты из правой таблицы, поэтому возвращаются.

Счет из левой таблицы, и возвращается 30, так как это значение относится к имени «Поток». Другие имена имеют значение NULL, поскольку они не относятся к имени «Поток».

Ниже приведен результат, который вы ожидали:

    SELECT  a.*, b.Score
FROM    @Table1 a
    LEFT JOIN @Table2 b
       ON a.ID = b.T1_ID 
WHERE 1=1
AND a.Name = 'Flow'

SQL применяет фильтр к правой таблице.

...